#pragma once

#include <metalc/block/cobj/_cobj.h>

typedef struct cobj {
	size_t size_used;
	union {
		struct cobj *prev;
		size_t prev_used;
	};
} cobj_t;
/* cobj内部存储，至少有一个 */
SAI uvoid *cobjin_set_cobj(void *ptr, size_t size, bool_t used){
	cobj_t *cobj = (cobj_t *)ptr;
	cobj_t *next = (cobj_t *)((paddr_t)ptr + size);
	cobj->size_used = size | (used & 0x1UL);
	/* 使用才会通知下一个 */
	if (used) next->prev_used = (paddr_t)next->prev | 0x1UL;
	return (void *)((paddr_t)ptr + sizeof(cobj_t));
}
SAI size_t cobjin_ptr_getsize(void *ptr){
	cobj_t *cobj = (cobj_t *)ptr;
	// if (!(cobj->size_used & 0x1UL)) return 0;
	return cobj->size_used & (~0x1UL);
}
SAI size_t cobjin_mocupysz(){
	return sizeof(cobj_t);
}
SAI uvoid* cobjin_ptr2uptr(void *ptr){
	return (uvoid *)((paddr_t)ptr + cobjin_mocupysz());
}
SAI void* cobjin_uptr2ptr(uvoid *uptr){
	cobj_t *cobj = (cobj_t *)((paddr_t)uptr - sizeof(cobj_t));
	return (void *)cobj;
}
SAI void* cobjin_get_prev(void *fst, size_t fstsize){
	PARAM_USED(fstsize);
	cobj_t *c = (cobj_t *)fst;
	cobj_t *prev = c->prev;
	/* 已被占用 */
	if ((size_t)prev & 0x1UL) return NULL_PTR;
	return (void *)prev;
}
SAI size_t cobjin_merge_next(void *fst, size_t fstsize){
	/* 尝试合并，只有下一个是非使用的时候才能被合并 */
	cobj_t *cur = (cobj_t *)fst;
	cobj_t *next = (cobj_t *)((paddr_t)fst + fstsize);
	/* 当前或者下一个内存块被使用，停止合并 */
	if (cur->size_used & 0x1UL) return fstsize;
	if (next->size_used & 0x1UL){ 
		/* 下一个内存块被使用 */
		/* 把prev_used设置为0 */
		next->prev = cur;
		return fstsize;
	}
	/* 合并成功 */
	cur->size_used = fstsize + next->size_used;
	/* 并且通知一个 */
	// if((cur->size_used & 0x1UL) != 0) printf("??? %lx, %lx, %lx\n", cur->size_used, fstsize, next->size_used);
	ASSERT((cur->size_used & 0x1UL) == 0);
	cobj_t *nnext = (cobj_t *)((paddr_t)next + next->size_used);
	nnext->prev = cur;
	return cur->size_used;
}
/* 切割头部 */
SAI void *cobjin_split(void *ptr, size_t size, size_t newsize, bool_t link_next){
	if (newsize + sizeof(cobj_t) > size) return NULL; /* 无法切割 */
	cobj_t *cur = (cobj_t *)ptr;
	cobj_t *ori_next = (cobj_t *)((paddr_t)ptr + size);
	cobj_t *next = (cobj_t *)((paddr_t)ptr + newsize);
	cur->size_used = newsize;
	next->size_used = size - newsize;
	next->prev = cur;

	/* 通知下下一个 */
	if (link_next) ori_next->prev = next;
	
	return (void *)next;
}
NEW_OUFUN(cobj, cobjin, cobjin_set_cobj, cobjin_ptr_getsize, cobjin_mocupysz, cobjin_ptr2uptr, cobjin_uptr2ptr, cobjin_get_prev, cobjin_merge_next, cobjin_split)